サービス境界とアクセスレベルで、特定のユーザーやサービスアカウントからしか BigQuery にアクセスできないようにしてみた
こんにちは、みかみです。
IAM ユーザーやサービスアカウントからの BigQuery へのアクセスはロールでも制限できますが、BigQuery や GCS などのリソースにサービス境界( VPC Service Controls )を指定しておけば、都度細かくロール設定せずともサービス境界外のアクセスは IN/OUT ともに制限することができます。
やりたいこと
- サービス境界を使用して、特定の IAM ユーザーやサービスアカウントからしか BigQuery と GCS にアクセスできないようにしたい
- Access Context Manager でメンバー属性を指定したアクセスレベルを作成してサービス境界に付与したい
前提
作業用アカウントには、サービス境界およびアクセスレベルの作成権限を付与済みです。
また、gcloud
コマンドと BigQuery Python クライアントライブラリが実行できる作業環境を準備済みです。
アクセスレベルを作成
特定の IAM ユーザーやサービスアカウントからのアクセスのみ許可するように、メンバー属性を指定したアクセスレベルを作成します。
2020/06/22 現在、GCP 管理コンソールからメンバー属性を指定したアクセスレベルの作成はできないとのことなので、gcloud
コマンドで作成します。
- アクセスレベルの作成 | Access Context Manager ドキュメント
- アクセスレベルの属性 | Access Context Manager ドキュメント
- アクセスレベルの YAML の例 | Access Context Manager ドキュメント
IAM ユーザーとサービスアカウント情報を記載した、以下の YAML ファイルを準備しました。
- members: - user:xxxx@classmethod.jp - serviceAccount:bq-data-owner@cm-da-mikami-yuki-258308.iam.gserviceaccount.com
※ユーザー情報は一部伏字に変更しています。
上記 YAML を指定して、gcloud
コマンドでアクセスレベルを作成します。
(test_bq) [ec2-user@ip-10-0-43-239 ~]$ gcloud access-context-manager levels create cm_mikami_member_access \ > --basic-level-spec=memberspec.yaml \ > --combine-function=OR \ > --description='Access level for test member permission.' \ > --title='Access_Level for Members' API [accesscontextmanager.googleapis.com] not enabled on project [797147019523]. Would you like to enable and retry (this will take a few minutes)? (y/N)? y Enabling service [accesscontextmanager.googleapis.com] on project [797147019523]... Operation "operations/acf.bfbd3280-6594-415d-8396-bcdb9d1e859c" finished successfully. Create request issued for: [cm_mikami_member_access] Waiting for operation [operations/accessPolicies/1079020659323/accessLevels/cm_mikami_member_access/create/1592819762890932] to complete...done. Created level [cm_mikami_member_access]. To take a quick anonymous survey, run: $ gcloud survey
Access Context Manager API が有効になっていない場合は、有効にしてリトライするかどうかの確認が表示されるので、y
を入力してリトライしました。
GCP 管理コンソールからも、アクセスレベルが作成されたことが確認できました。
サービス境界を作成
GCP 管理コンソールナビゲーションメニューの「セキュリティ」から「VPC Service Controls」を選択し、「新しい境界」をクリックします。
「境界名」に任意の名前を入力し、「プロジェクトを追加」ボタンからアクセス制御したいプロジェクトを追加。「サービスを追加」ボタンから、「BigQuery API」と「Google Cloud Storage API」を追加し、「上りポリシー: アクセスレベル」で先ほど作成したメンバー指定のアクセスレベルを選択して「境界を作成」ボタンをクリック。
サービス境界が作成できました。
IAM ユーザーで GCP 管理コンソールから BigQuery にアクセス
アクセスレベルで指定した IAM ユーザーで、BigQuery 管理画面からアクセスしてみます。
問題なくデータが参照できました。
続いてアクセスレベルで指定していない IAM ユーザーでアクセスしてみます。
確認する IAM ユーザには、対象のプロジェクトの閲覧者と BigQuery データ編集者のロールを付与済みで、サービス境界作成前には BigQuery のデータを参照できることを確認済みです。
アクセスレベルで指定した通り、許可していない IAM ユーザーでは BigQuery にアクセスできません。
データセットを新規作成しようとしても、エラーとなり作成できません。
GCS を参照しようとしても、同様にアクセスできないことが確認できました。
サービスアカウントで Python クライアントライブラリから BigQuery にアクセス
サービスアカウントからのアクセス制御も確認してみます。
Python クライアントライブラリ経由で BigQuery へデータをロードし、ロードしたデータを参照後にテーブルを削除する、以下の Python コードを準備しました。
from google.cloud import bigquery from google.oauth2 import service_account import argparse import os.path parser = argparse.ArgumentParser(description='job') parser.add_argument('file', help='account key file') args = parser.parse_args() key_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), args.file) credentials = service_account.Credentials.from_service_account_file( key_path, scopes=["https://www.googleapis.com/auth/cloud-platform"], ) client = bigquery.Client( credentials=credentials, project=credentials.project_id, ) table_id = '{}.dataset_1.data_sample'.format(credentials.project_id) table = bigquery.Table(table_id) # load data job_config = bigquery.LoadJobConfig() job_config.source_format = bigquery.SourceFormat.CSV job_config.skip_leading_rows = 1 job_config.autodetect = True # from url uri = "gs://test-mikami/data_sample.csv" job = client.load_table_from_uri(uri, table, job_config=job_config) print("\tStarting job {}".format(job.job_id)) job.result() print("table: {} Loaded from uri.".format(table.table_id)) # exec query query = ('SELECT * FROM `{}` LIMIT 10'.format(table_id)) query_job = client.query(query) rows = query_job.result() for row in rows: print(row) print("Selected data") # delete tables client.delete_table(table) print("Deleted table {}.{}.{}".format(table.project, table.dataset_id, table.table_id))
まずはアクセスレベルで許可設定済みのサービスアカウントで実行してみます。
(test_bq) [ec2-user@ip-10-0-43-239 test_role]$ python test_access_level.py keys/bq-data-owner.json Starting job 9deeb2a2-4962-454e-a531-3f91cc79821e table: data_sample Loaded from uri. Row((1, 'value1'), {'col_1': 0, '_col_2': 1}) Row((2, 'value2'), {'col_1': 0, '_col_2': 1}) Row((3, 'value3'), {'col_1': 0, '_col_2': 1}) Selected data Deleted table cm-da-mikami-yuki-258308.dataset_1.data_sample
GCS からの データロード、ロードデータの参照、テーブル削除全て問題なく実行できました。
続いて、アクセスレベルでは指定していない、BigQuery 管理者ロールを付与済みのサービスアカウントで実行してみます。
(test_bq) [ec2-user@ip-10-0-43-239 test_role]$ python test_access_level.py keys/bq-admin.json Traceback (most recent call last): File "test_access_level.py", line 29, in <module> job = client.load_table_from_uri(uri, table, job_config=job_config) File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/bigquery/client.py", line 1593, in load_table_from_uri load_job._begin(retry=retry, timeout=timeout) File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/bigquery/job.py", line 640, in _begin retry, method="POST", path=path, data=self.to_api_repr(), timeout=timeout File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/bigquery/client.py", line 556, in _call_api return call() File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/api_core/retry.py", line 286, in retry_wrapped_func on_error=on_error, File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/api_core/retry.py", line 184, in retry_target return target() File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/_http.py", line 423, in api_request raise exceptions.from_http_response(response) google.api_core.exceptions.Forbidden: 403 POST https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/jobs: VPC Service Controls: Request is prohibited by organization's policy. vpcServiceControlsUniqueIdentifier: 6458ac92e9fb05cb.
VPC Service Controls: Request is prohibited by organization's policy.
とのことで、アクセスレベルの指定通り、許可されていないサービスアカウントからのアクセスはできないことが確認できました。
まとめ(所感)
特に個人情報などの重要データを保存する可能性があるプロジェクトの場合、VPC Service Controls と Access Context Manager でサービス境界外からのアクセス制御を指定しておいた方がより安全です。
Access Context Manager で設定するアクセスレベルには、今回確認したメンバー属性の他にも、IP サブネットワークやデバイス ポリシーなどの指定もできるので、要件に合ったアクセス制御を柔軟に実現できそうです。